#pragma once
#include<iostream>
#include<functional>
namespace MY
{
	template<class T>
	class auto_ptr
	{
	public:
		explicit auto_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{
		}
		auto_ptr(auto_ptr& ap)
			:_ptr(ap._ptr)
		{
			// 权限转移
			ap._ptr = nullptr;
		}
		auto_ptr& operator=(auto_ptr& ap)
		{
			if (this != &ap)
			{
				if (_ptr) delete _ptr;
				// 权限转移
				_ptr = ap._ptr;
				ap._ptr = nullptr;
			}
			return *this;
		}
		auto_ptr(auto_ptr&& x)
			:_ptr(x._ptr)
		{
			// 权限转移
			x._ptr = nullptr;
		}
		auto_ptr& operator=(auto_ptr&& x)
		{
			delete _ptr;
			// 权限转移
			_ptr = x._ptr;
			x._ptr = nullptr;
			return *this;
		}
		~auto_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator[](size_t i)
		{
			return _ptr[i];
		}
	private:
		T* _ptr;
	};

	template<class T>
	class unique_ptr
	{
	public:
		explicit unique_ptr(T* ptr = nullptr)
			:_ptr(ptr)
		{
		}

		unique_ptr(const unique_ptr& up) = delete;
		unique_ptr& operator=(const unique_ptr& up) = delete;

		unique_ptr(unique_ptr&& up) noexcept
		{
			_ptr = up._ptr;
			up._ptr = nullptr;
		}

		unique_ptr& operator=(unique_ptr&& x) noexcept
		{
			delete _ptr;
			_ptr = x._ptr;
			x._ptr = nullptr;
			return *this;
		}
		~unique_ptr()
		{
			if (_ptr)
			{
				cout << "delete:" << _ptr << endl;
				delete _ptr;
			}
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator[](size_t i)
		{
			return _ptr[i];
		}
	private:
		T* _ptr;
	};

	template<class T>
	class shared_ptr
	{
	public:
		shared_ptr()
			:_ptr(nullptr),
			_pcount(nullptr)
		{}
		explicit shared_ptr(T* ptr)
			:_ptr(ptr),
			_pcount(new atomic<int>(1))
		{}
		template<class D>
		explicit shared_ptr(T* ptr, D& del)
			:_ptr(ptr),
			_pcount(new atomic<int>(1)),
			_del(del)
		{}
		~shared_ptr()
		{
			release();
		}
		shared_ptr(const shared_ptr& sp)
			:_ptr(sp._ptr),
			_pcount(sp._pcount),
			_del(sp._del)
		{
			++(*_pcount);
		}
		void release()
		{
			if (--(*_pcount) == 0)
			{
				_del(_ptr);
				delete _pcount;
				_ptr = nullptr;
				_pcount = nullptr;
			}
		}
		shared_ptr& operator=(const shared_ptr& sp)
		{
			if (this != &sp)
			{
				release();
				_ptr = sp._ptr;
				_pcount = sp._pcount;
				++(*_pcount);
				_del = sp._del;
			}
			return *this;
		}
		void swap(shared_ptr& sp)
		{
			std::swap(sp._ptr);
			std::swap(sp._pcount);
			std::swap(_del, sp._del);
		}
		shared_ptr(shared_ptr&& sp)
		{
			swap(sp);
		}
		shared_ptr& operator=(shared_ptr&& sp)
		{
			release();
			swap(sp);
			return *this;
		}
		T& operator*()
		{
			return *_ptr;
		}
		T* operator->()
		{
			return _ptr;
		}
		T& operator[](size_t i)
		{
			return _ptr[i];
		}
		T* get() const
		{
			return _ptr;
		}
		int use_count() const
		{
			return *_pcount;
		}
		explicit operator bool() const
		{
			return _ptr != nullptr;
		}
	private:
		T* _ptr;
		//int* _pcount;
		atomic<int>* _pcount;
		function<void(T*)> _del = [](T* ptr) {delete ptr; };
	};

	template<class T>
	class weak_ptr
	{
	public:
		weak_ptr()
		{
		}
		weak_ptr(const shared_ptr<T>& sp)
			:_ptr(sp.get())
		{
		}
		weak_ptr<T>& operator= (const shared_ptr<T>& sp)
		{
			_ptr = sp.get();
			return *this;
		}
	private:
		T* _ptr = nullptr;
	};
}